﻿using System;
using System.Linq;
using System.Reflection;
using System.Collections.Generic;

namespace Argos.Tracking
{
	/// <summary>
	/// A tracking service that tracks the workflow instance IDs of
	/// target monitoring workflow instances.
	/// </summary>
	public sealed class TargetInstanceTrackingService : PropertyTrackingServiceBase
	{

		public const string TargetIdPropertyName = "TargetId";



		/// <summary>
		/// A dictionary that contains instance ID - target ID pairs where
		/// the key is the instance ID and the value is the target ID.
		/// </summary>
		private Dictionary<Guid, Guid> InstanceTargetIds = new Dictionary<Guid, Guid>();

		private object SyncRoot = new object();



		#region public Guid? GetTargetId(Guid workflowInstanceId)

		/// <summary>
		/// Returns the target ID for the given workflow instance.
		/// </summary>
		/// <param name="workflowInstanceId">The ID of the workflow instance whose target ID to return.</param>
		public Guid? GetTargetId(Guid workflowInstanceId)
		{
			if (this.InstanceTargetIds.ContainsKey(workflowInstanceId))
			{
				return this.InstanceTargetIds[workflowInstanceId];
			}
			return null;
		}

		#endregion
		
		#region public Guid? GetWorkflowInstanceId(Guid targetId)

		/// <summary>
		/// Returns the workflow instance ID of the given target.
		/// </summary>
		/// <param name="targetId">The ID of the target whose running monitoring workflow instance to return.</param>
		public Guid? GetWorkflowInstanceId(Guid targetId)
		{
			bool found = false;

			Func<KeyValuePair<Guid, Guid>, bool> finder = delegate(KeyValuePair<Guid, Guid> pair)
			{
				if (pair.Value == targetId)
				{
					found = true;
					return true;
				}
				return false;
			};

			KeyValuePair<Guid, Guid> pair2;
			lock (this.SyncRoot)
			{
				pair2 = this.InstanceTargetIds.FirstOrDefault(finder);
			}
			if (found)
			{
				return pair2.Key;
			}

			return null;
		}

		#endregion



		#region protected override bool IsPropertyTracked(PropertyInfo property)

		/// <summary>
		/// Returns <c>true</c> only for properties whose name equals <c>TargetId</c>.
		/// </summary>
		protected override bool IsPropertyTracked(PropertyInfo property)
		{
			return property.Name == TargetIdPropertyName;
		}

		#endregion

		#region protected override bool IsWorkflowInstanceTracked(Guid instanceId, Type workflowType)

		/// <summary>
		/// Returns <c>true</c> only for workflow instances that are target monitoring workflows.
		/// </summary>
		protected override bool IsWorkflowInstanceTracked(Guid instanceId, Type workflowType)
		{
			return workflowType.IsTargetMonitoringWorkflow();
		}

		#endregion

		#region protected override void OnWorkflowPropertyChanged(WorkflowPropertyChangedEventArgs e)

		/// <summary>
		/// Handles the change of the value of a property in a workflow instance.
		/// </summary>
		/// <param name="e">The arguments for the event.</param>
		protected override void OnWorkflowPropertyChanged(WorkflowPropertyChangedEventArgs e)
		{
			base.OnWorkflowPropertyChanged(e);

			if (e.PropertyName == TargetIdPropertyName && e.PropertyValue is Guid)
			{
				if (this.InstanceTargetIds.ContainsKey(e.InstanceId))
				{
					this.InstanceTargetIds[e.InstanceId] = (Guid)e.PropertyValue;
				}
				else
				{
					this.InstanceTargetIds.Add(e.InstanceId, (Guid)e.PropertyValue);
				}
			}
		}

		#endregion

	}
}
